﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.IO;
using Algoritmia.Problems.Puzzles;

namespace Demos.Puzzles.EightPuzzle
{
    class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private string _historial;
        public string Historial
        {
            get { return _historial; }
            set
            {
                _historial = value;
                NotifyPropertyChanged("Historial");
            }
        }

        public ObservableCollection<UIElement> ContenidoPuzzle { get; set; }
        public double MaxX { get; set; }
        public double MaxY { get; set; }

        public ICommand CargarImagenCommand { get; private set; }
        public ICommand DesordenarCommand { get; private set; }
        public ICommand SolucionarCommand { get; private set; }
        public ICommand RedimensionarCommand { get; private set; }

        private List<int> _puzzle;
        private List<KeyValuePair<int, int>> _movimientos;
        static private Random _r;
        private int _duracion; //ms
        const int altoPieza = 200;
        const int anchoPieza = 150;


        public ViewModel()
        {
            _duracion = 300;
            _puzzle = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
            _movimientos = new List<KeyValuePair<int, int>>();
            _r = new Random(DateTime.Now.Millisecond);

            ContenidoPuzzle = new ObservableCollection<UIElement>();
            Historial = "";

            CargarImagenCommand = new RelayCommand(CargarImagen, () => true);
            DesordenarCommand = new RelayCommand(Desordenar, () => ContenidoPuzzle.Count > 0);
            SolucionarCommand = new RelayCommand(Solucionar, () => ContenidoPuzzle.Count > 0);
            RedimensionarCommand = new RelayCommand(Redimensionar, () => ContenidoPuzzle.Count > 0);
        }


        private void Desordenar()
        {
            const int numMovimientos = 10;

            for (int i = 0; i < numMovimientos; i++)
            {
                int pieza = 0, vecina = 0;
                RandomMove(ref pieza, ref vecina);
                _movimientos.Add(new KeyValuePair<int, int>(pieza, vecina));
            }
            Mover(this, new EventArgs());
        }

        public KeyValuePair<int,int> NextMove()
        {
            var mov = _movimientos[0];
            _movimientos.RemoveAt(0);
            return mov;
        }

        public int CountMoves()
        {
            return _movimientos.Count;
        }

        public int ObtenerPieza(int pos)
        {
            return _puzzle[pos];
        }

        public void ReordenarPiezas(KeyValuePair<int, int> ultimoMovimiento)
        {
            var aux = _puzzle[ultimoMovimiento.Key];
            _puzzle[ultimoMovimiento.Key] = _puzzle[ultimoMovimiento.Value];
            _puzzle[ultimoMovimiento.Value] = aux;
        }

        public static void RandomMove(ref int pieza, ref int vecina)
        {
            List<int> vecinas = new List<int>();
            vecina = -1;
            pieza = _r.Next(0, 9);
            switch (pieza)
            {
                case 0:
                    vecinas = new List<int> {1, 3};
                    break;
                case 1:
                    vecinas = new List<int> { 0, 4, 2 };
                    break;
                case 2:
                    vecinas = new List<int> {1, 5 };
                    break;
                case 3:
                    vecinas = new List<int> { 0, 4, 6 };
                    break;
                case 4:
                    vecinas = new List<int> { 1,3,5,7 };
                    break;
                case 5:
                    vecinas = new List<int> { 2,4,8 };
                    break;
                case 6:
                    vecinas = new List<int> { 3,7 };
                    break;
                case 7:
                    vecinas = new List<int> { 4,6,8 };
                    break;
                case 8:
                    vecinas = new List<int> { 5,7 };
                    break;
            }

            int indice = 0;
            while (vecina == -1)
            {
                int eleccion = _r.Next(0, 2);
                if (eleccion == 1)
                {
                    vecina = vecinas[indice];
                }

                indice++;
                if (indice >= vecinas.Count)
                {
                    indice = 0;
                }
            }
        }

        public void Solucionar()
        {
            var space = new Algoritmia.Problems.Puzzles.EightPuzzle(new Board(_puzzle));
            var solver =
                new Algoritmia.Schemes.BacktrackingEnumerator
                    <Algoritmia.Problems.Puzzles.EightPuzzle.State, Algoritmia.Problems.Puzzles.EightPuzzle,
                        Algoritmia.Problems.Puzzles.EightPuzzle.State>(null);
            //solver.Enumerate(new Algoritmia.StateSpace.IForwardStateSpace<Algoritmia.Problems.Puzzles.EightPuzzle, Algoritmia.Problems.Puzzles.EightPuzzle.State>());
            Mover(this, new EventArgs());
        }

        private void CargarImagen()
        {
            Historial = "";
            ContenidoPuzzle.Clear();
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    var pieza = new Border();
                    BitmapImage pedazo;
                    var foto = new System.Windows.Controls.Image();

                    if (i != 0 || j != 2)
                    {
                        pedazo = ImageCutter.Cut(@"media\8puzzle\perro.jpg", altoPieza, anchoPieza, i, j);
                        foto.Source = pedazo;
                    }
                    pieza.Child = foto;
                    pieza.BorderBrush = System.Windows.Media.Brushes.Black;
                    pieza.BorderThickness = new Thickness(1);
                    pieza.Width = MaxX / 3;
                    pieza.Height = MaxY / 3;
                    Canvas.SetLeft(pieza, pieza.Width * j);
                    Canvas.SetTop(pieza, pieza.Height * i);
                    ContenidoPuzzle.Add(pieza);
                }
            }
        }

        private void Redimensionar()
        {
            ContenidoPuzzle.Clear();
            var cont = 0;
            for (var i = 0; i < 3; i++)
            {
                for (var j = 0; j < 3; j++)
                {
                    var k = _puzzle[cont] / 3;
                    var l = _puzzle[cont] % 3;
                    var pieza = new Border();
                    BitmapImage pedazo;
                    var foto = new System.Windows.Controls.Image();

                    if (k != 0 || l != 2)
                    {
                        pedazo = ImageCutter.Cut(@"media\8puzzle\perro.jpg", altoPieza, anchoPieza, k, l);
                        foto.Source = pedazo;
                    }
                    pieza.Child = foto;
                    pieza.BorderBrush = System.Windows.Media.Brushes.Black;
                    pieza.BorderThickness = new Thickness(1);
                    pieza.Width = MaxX / 3;
                    pieza.Height = MaxY / 3;
                    Canvas.SetLeft(pieza, pieza.Width * j);
                    Canvas.SetTop(pieza, pieza.Height * i);
                    ContenidoPuzzle.Add(pieza);
                    cont++;
                }
            }
        }

        private void Mover(object sender, EventArgs e)
        {
            if (CountMoves() > 0)
            {
                var mov = NextMove();
                var pieza1 = ContenidoPuzzle[ObtenerPieza(mov.Key)];
                var pieza2 = ContenidoPuzzle[ObtenerPieza(mov.Value)];

                Historial += mov.Key + "-" + mov.Value + "\n";

                ReordenarPiezas(mov);

                var sb1 = new Storyboard();
                Duration duration1 = new Duration(TimeSpan.FromMilliseconds(_duracion));
                DoubleAnimation myDoubleAnimation1 = new DoubleAnimation();
                DoubleAnimation myDoubleAnimation2 = new DoubleAnimation();
                DoubleAnimation myDoubleAnimation3 = new DoubleAnimation();
                DoubleAnimation myDoubleAnimation4 = new DoubleAnimation();

                myDoubleAnimation1.Duration = duration1;
                myDoubleAnimation2.Duration = duration1;
                myDoubleAnimation3.Duration = duration1;
                myDoubleAnimation4.Duration = duration1;
                sb1.Duration = duration1;

                sb1.Children.Add(myDoubleAnimation1);
                sb1.Children.Add(myDoubleAnimation2);
                sb1.Children.Add(myDoubleAnimation3);
                sb1.Children.Add(myDoubleAnimation4);

                Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(Canvas.Left)"));
                Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(Canvas.Top)"));
                Storyboard.SetTargetProperty(myDoubleAnimation3, new PropertyPath("(Canvas.Left)"));
                Storyboard.SetTargetProperty(myDoubleAnimation4, new PropertyPath("(Canvas.Top)"));

                Storyboard.SetTarget(myDoubleAnimation1, pieza1);
                Storyboard.SetTarget(myDoubleAnimation2, pieza1);
                Storyboard.SetTarget(myDoubleAnimation3, pieza2);
                Storyboard.SetTarget(myDoubleAnimation4, pieza2);

                myDoubleAnimation1.From = Canvas.GetLeft(pieza1);
                myDoubleAnimation1.To = Canvas.GetLeft(pieza2);
                myDoubleAnimation2.From = Canvas.GetTop(pieza1);
                myDoubleAnimation2.To = Canvas.GetTop(pieza2);
                myDoubleAnimation3.From = Canvas.GetLeft(pieza2);
                myDoubleAnimation3.To = Canvas.GetLeft(pieza1);
                myDoubleAnimation4.From = Canvas.GetTop(pieza2);
                myDoubleAnimation4.To = Canvas.GetTop(pieza1);

                sb1.Completed += new EventHandler(Mover);
                sb1.Begin();
            }
        }
    }
}
